AWSSupport-StartEC2RescueWorkflow オートメーションを使う際の注意点
しばたです。
先週末からCrowdStrike社のセキュリティ対策製品であるCrowdStrike Falconの不具合を発端とする障害が世界中で発生しており、AWSにおいても当該製品を扱うWindows環境が影響を受けています。
AWSからはAWS Health Dashboardにおいて状況と対応策に関するアナウンスが行われています。
対応策に関して7/19~7/20の間に何度か更新され、7/20日の早朝ごろにre:Postの以下の投稿に別記される様になりました。
AWSSupport-StartEC2RescueWorkflow を使った復旧について
このre:Postの投稿では、影響を受けたEC2インスタンスに対して
-
- CrowdStrike社側の更新を受け再起動で復旧したインスタンスに対しては追加の対処不要
-
- 再起動で復旧しないインスタンスに関しては
EC2Rescue
を使い当該インスタンスで問題となっているファイルを削除する
- 再起動で復旧しないインスタンスに関しては
方法を提示しており、2.のEC2Rescue
の使用に関して
- 2-1. SSMオートメーション
AWSSupport-StartEC2RescueWorkflow
を使ったEC2Rescue
の自動実行 - 2-2. 手作業で復旧用インスタンスを作成し
EC2Rescue
を手動実行
の2パターン記載されています。
AWSSupport-StartEC2RescueWorkflow
は2-2.にある一連の手作業を自動化するものであり、障害からの復旧において人間によるミスを防ぐ点もあるので可能であればこちらを使う方が良いでしょう。
ただ、このAWSSupport-StartEC2RescueWorkflow
は初見ではちょっと扱いにくいため本記事で注意点を解説していきます。
免責事項
AWSSupport-StartEC2RescueWorkflow オートメーションを使う際の注意点
AWSSupport-StartEC2RescueWorkflow
オートメーションを使う際の注意点を5点紹介します。
1. インスタンスの種類
ドキュメントに
Important
Amazon EC2 instances created from Marketplace Amazon Machine Images (AMIs) are not supported by this automation.
との記述がありAWS Marketplaceからサブスクライブしているインスタンスに対してはこのオートメーションは使えません。
実行時チェックにより処理が中断されます。
恐らくこれは商用製品が導入されている場合のコンテンツ保護を意図したものだと予想します。
2. オートメーション実行時の権限
このオートメーションは、処理の内容が
- 処理用の一時EC2インスタンスを作成する
- このインスタンスはCloudFormationで作られる
- 併せて専用のIAMロールを作成しアタッチする
- 復旧対象のEC2を停止し、EBSボリュームをアタッチしなおした上マウントする
- EBSボリュームが暗号化されている場合はKMSに対する権限も必要
というものであるため実行時にかなり多くの権限を必要とします。
厳密にはAmazonSSMAutomationRole
マネージドポリシーの内容に加え、次の権限が必要となります。
クリックして展開
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"lambda:InvokeFunction",
"lambda:DeleteFunction",
"lambda:GetFunction"
],
"Resource": "arn:aws:lambda:*:<An-AWS-Account-ID>:function:AWSSupport-EC2Rescue-*",
"Effect": "Allow"
},
{
"Action": [
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": [
"arn:aws:s3:::awssupport-ssm.*/*.template",
"arn:aws:s3:::awssupport-ssm.*/*.zip"
],
"Effect": "Allow"
},
{
"Action": [
"iam:CreateRole",
"iam:CreateInstanceProfile",
"iam:GetRole",
"iam:GetInstanceProfile",
"iam:PutRolePolicy",
"iam:DetachRolePolicy",
"iam:AttachRolePolicy",
"iam:PassRole",
"iam:AddRoleToInstanceProfile",
"iam:RemoveRoleFromInstanceProfile",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DeleteInstanceProfile"
],
"Resource": [
"arn:aws:iam::<An-AWS-Account-ID>:role/AWSSupport-EC2Rescue-*",
"arn:aws:iam::<An-AWS-Account-ID>:instance-profile/AWSSupport-EC2Rescue-*"
],
"Effect": "Allow"
},
{
"Action": [
"lambda:CreateFunction",
"ec2:CreateVpc",
"ec2:ModifyVpcAttribute",
"ec2:DeleteVpc",
"ec2:CreateInternetGateway",
"ec2:AttachInternetGateway",
"ec2:DetachInternetGateway",
"ec2:DeleteInternetGateway",
"ec2:CreateSubnet",
"ec2:DeleteSubnet",
"ec2:CreateRoute",
"ec2:DeleteRoute",
"ec2:CreateRouteTable",
"ec2:AssociateRouteTable",
"ec2:DisassociateRouteTable",
"ec2:DeleteRouteTable",
"ec2:CreateVpcEndpoint",
"ec2:DeleteVpcEndpoints",
"ec2:ModifyVpcEndpoint",
"ec2:Describe*"
],
"Resource": "*",
"Effect": "Allow"
}
]
}
そして実際に動作確認したところ、これでも足りず、追加で
autoscaling:DescribeAutoScalingInstances
ec2:CreateLaunchTemplate
ec2:DeleteLaunchTemplate
iam:TagRole
cloudformation:DescribeStackResource
ec2:ModifyInstanceAttribute
ec2:DetachVolume
ec2:AttachVolume
ec2:DescribeVolumes
も必要でした...
恐らくオートメーション本体の更新に対してドキュメントの修正が間に合っていない雰囲気なのですが、これは緊急時に必要となるものなので正直よろしくないですね。
緊急時にこれらの権限まわりを厳密に設定することは難しいと思いますので、事前に専用のロールを用意しておくことをお勧めします。
一応簡単なCloudFormation Templateを用意しておいたので適宜改変してご利用ください。
AWSTemplateFormatVersion: 2010-09-09
Description: IAM Role for AWSSupport-StartEC2RescueWorkflow automation invocation.
Parameters:
RoleName:
Description: "Input role name."
Type: String
Default: "StartEC2RescueWorkflowRole"
Resources:
# IAM Role
EC2RescueWorkflowRole:
Type: AWS::IAM::Role
Properties:
RoleName:
Fn::Sub: "${RoleName}"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- "ssm.amazonaws.com"
Action:
- "sts:AssumeRole"
Path: "/"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole"
# Inline policy
EC2RescueWorkflowPolicy:
Type: AWS::IAM::RolePolicy
Properties:
RoleName:
Ref: EC2RescueWorkflowRole
PolicyName: "StartEC2RescueWorkflowPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "lambda:InvokeFunction"
- "lambda:DeleteFunction"
- "lambda:GetFunction"
Resource:
Fn::Sub: "arn:${AWS::Partition}:lambda:*:${AWS::AccountId}:function:AWSSupport-EC2Rescue-*"
- Effect: "Allow"
Action:
- "s3:GetObject"
- "s3:GetObjectVersion"
Resource:
- "arn:aws:s3:::awssupport-ssm.*/*.template"
- "arn:aws:s3:::awssupport-ssm.*/*.zip"
- Effect: "Allow"
Action:
- "iam:CreateRole"
- "iam:CreateInstanceProfile"
- "iam:GetRole"
- "iam:GetInstanceProfile"
- "iam:PutRolePolicy"
- "iam:DetachRolePolicy"
- "iam:AttachRolePolicy"
- "iam:PassRole"
- "iam:AddRoleToInstanceProfile"
- "iam:RemoveRoleFromInstanceProfile"
- "iam:DeleteRole"
- "iam:DeleteRolePolicy"
- "iam:DeleteInstanceProfile"
- "iam:TagRole"
Resource:
- Fn::Sub: "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/AWSSupport-EC2Rescue-*"
- Fn::Sub: "arn:${AWS::Partition}:iam::${AWS::AccountId}:instance-profile/AWSSupport-EC2Rescue-*"
- Effect: "Allow"
Action:
- "lambda:CreateFunction"
- "ec2:CreateVpc"
- "ec2:ModifyVpcAttribute"
- "ec2:DeleteVpc"
- "ec2:CreateInternetGateway"
- "ec2:AttachInternetGateway"
- "ec2:DetachInternetGateway"
- "ec2:DeleteInternetGateway"
- "ec2:CreateSubnet"
- "ec2:DeleteSubnet"
- "ec2:CreateRoute"
- "ec2:DeleteRoute"
- "ec2:CreateRouteTable"
- "ec2:AssociateRouteTable"
- "ec2:DisassociateRouteTable"
- "ec2:DeleteRouteTable"
- "ec2:CreateVpcEndpoint"
- "ec2:DeleteVpcEndpoints"
- "ec2:ModifyVpcEndpoint"
- "ec2:Describe*"
Resource: "*"
- Effect: "Allow"
Action:
- "autoscaling:DescribeAutoScalingInstances"
- "ec2:CreateLaunchTemplate"
- "ec2:DeleteLaunchTemplate"
- "cloudformation:DescribeStackResource"
- "ec2:ModifyInstanceAttribute"
- "ec2:DetachVolume"
- "ec2:AttachVolume"
- "ec2:DescribeVolumes"
Resource: "*"
3. 一時EC2インスタンスはSSMのマネージドインスタンスになる (要エンドポイントアクセス)
SSMを使って処理を行う都合、一時EC2インスタンスはSSMのマネージドインスタンスである必要があります。
すなわち、
- EC2インスタンスがインターネットアクセス可能
- EC2インスタンスがSSMのVPC Endpointへアクセス可能
のどちらかを満たす必要があります。
オートメーション実行時にEC2を配置するサブネット[2]やグローバルIP付与の有無を指定可能ですので環境に合わせた内容にしてください。
4. 一時EC2インスタンスのセキュリティグループ指定
オートメーション実行時に一時EC2インスタンスに割り当てるセキュリティグループを指定可能です。
指定しなかった場合デフォルトのDefault
セキュリティグループが選ばれます。
Security Hubの指摘によりDefault
セキュリティグループの通信を制限している場合、独自のセキュリティグループを明示しないと一切のアウトバウント通信ができずタイムアウトエラーとなりますのでご注意ください。
5. 実行するスクリプトのエンコーディング
このオートメーションでは復旧用のEBSボリュームに対する処理をPowerShellで記述することができます。
記述したスクリプトの内容はBASE64エンコードした形で引数として指定します。
ここで、ドキュメント上はスクリプトのエンコーディングをUnicode (UTF-16)
で扱う記述となっているのですが、オートメーション側ではASCII
固定でデコードします。
# ・・・前略・・・
# オートメーション側はASCIIコードの想定でBASE64エンコードされた文字列をデコードしてしまう
" $scriptblock = [scriptblock]::Create([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String('・・・BASE64エンコードされた文字列・・・')))",
" & $scriptblock",
# ・・・後略・・・
このため公式ドキュメントの内容に倣うと実行時エラーとなってしまいます。
# 公式ドキュメントの間違った記述 (Unicodeでエンコードされている)
[System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes([System.IO.File]::ReadAllText('PATH_TO_FILE')))
正しくは以下の様にASCII
でエンコードしてください。
# 指定側も ASCII でエンコードしてやる
[System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes([System.IO.File]::ReadAllText('PATH_TO_FILE')))
これは明確なバグですのであとでAWSへフィードバックしておこうと思います。
なので将来的には修正されるかもしれません。
動作確認
ここからは実際に動作確認をしていきます。
内容としてはAWSの指示に従い今回の障害への対応「Option 1 - Use the AWS Systems Manager automation runbook」を行います。
0. 検証環境
今回は私の検証用AWSアカウントの東京リージョンにVPCを一つ用意し、そこにある日本語版Windows Server 2022 EC2インスタンスが障害を起こした体とします。
- EC2のAMIは本日時点で最新の
ami-082a6b0eaa477ae3d (Windows_Server-2022-Japanese-Full-Base-2024.07.10)
としています
このインスタンスに対しAWS指定の手順に従いAWSSupport-StartEC2RescueWorkflow
オートメーションを実行して問題を起こしているファイルを削除し復旧を試みます。
本日時点では厳密に環境を再現できないため、通常起動させたEC2に「C:\Windows\System32\drivers\CrowdStrike\C-00000291-xxxxxxxx-xxxxxxxx.sys
」というダミーファイルを配置し、このファイルが消えたことをもって復旧した扱いとします。
「CrowdStrike-Broken」という名前で障害インスタンスを用意
削除予定ファイルをあらかじめ配備ずみ
1. 専用IAMロールの準備
前節で説明したオートメーション用のIAMロールはStartEC2RescueWorkflowRole
という名前で事前に作成ずみです。
2. 実行スクリプトの準備
オートメーションを実行する前に最初に実行するスクリプトの準備を行います。
AWSの手順としては、以下のコマンドを使い障害の原因となるファイルを削除する手順となっています。
get-childitem -path "$env:EC2RESCUE_OFFLINE_DRIVE\Windows\System32\drivers\CrowdStrike\" -Include C-00000291*.sys -Recurse | foreach { $_.Delete()}
オートメーションで使うにはこの内容をBASE64エンコーディングする必要があります。
PowerShellコンソールを起動し、以下の手順で得られる$encodedCommand
の内容を取得します。
# AWSドキュメントの手順とは異なり、エンコーディングを ASCII にすること
$command = 'get-childitem -path "$env:EC2RESCUE_OFFLINE_DRIVE\Windows\System32\drivers\CrowdStrike\" -Include C-00000291*.sys -Recurse | foreach { $_.Delete()}'
$encodedCommand = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($command))
$encodedCommand
ここで得られた
Z2V0LWNoaWxkaXRlbSAtcGF0aCAiJGVudjpFQzJSRVNDVUVfT0ZGTElORV9EUklWRVxXaW5kb3dzXFN5c3RlbTMyXGRyaXZlcnNcQ3Jvd2RTdHJpa2VcIiAtSW5jbHVkZSBDLTAwMDAwMjkxKi5zeXMgLVJlY3Vyc2UgfCBmb3JlYWNoIHsgJF8uRGVsZXRlKCl9
を後で使います。
余談1 : エンコード結果の検証
エンコード結果の検証は以下の様に行ってください。
# BASE64文字列をデコードして検証
[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($encodedCommand))
AWS提供の手順ではUTF-8前提となっており、厳密には誤った指定なのですが、今回に関してはこちらでも問題ありません。
# AWSの手順ではUTF-8文字列を扱う形になっているが、今回に関してはASCIIと同様の結果を得られるので問題ない
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("REPLACE_WITH_BASE64_HERE"))
余談2 : Unicode を使った場合のコマンド
AWSドキュメントに従いUnicode文字列をBASE64エンコードした結果は以下となり、前述のものと異なることが分かります。
# DO NOT USE : これは使わないこと
ZwBlAHQALQBjAGgAaQBsAGQAaQB0AGUAbQAgAC0AcABhAHQAaAAgACIAJABlAG4AdgA6AEUAQwAyAFIARQBTAEMAVQBFAF8ATwBGAEYATABJAE4ARQBfAEQAUgBJAFYARQBcAFcAaQBuAGQAbwB3AHMAXABTAHkAcwB0AGUAbQAzADIAXABkAHIAaQB2AGUAcgBzAFwAQwByAG8AdwBkAFMAdAByAGkAawBlAFwAIgAgAC0ASQBuAGMAbAB1AGQAZQAgAEMALQAwADAAMAAwADAAMgA5ADEAKgAuAHMAeQBzAA==
なお、誤ってこちらを使っても「gというコマンドは無い」という旨のエラー[3]となるだけで副作用はありませんのでご安心ください。
The term 'g' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
3. オートメーションの実行
マネジメントコンソールから「AWS Systems Manager」→「オートーメーション」を選択し、新規の「Execution automation」をクリックします。
ランブック一覧からAWSSupport-StartEC2RescueWorkflow
を選んで選択すると、
画面下部に詳細が表示されるのでそのまま「次へ」進みます。
今回は単一環境への実行となるため「Simple execution」を選びます。
入力パラメーターの指定で最初に復旧対象のEC2インスタンスを選択します。
今回はInteractive pickerを使っていますが、あらかじめインスタンスIDが分かっている場合はこれを使わず直接インスタンスIDを指定することもできます。
その他のパラメーターは環境に応じた内容にします。
特に注意すべきパラメータに関しては下表のとおりです。
パラメーター名 | 指定内容 | 備考 |
---|---|---|
AutomationAssumeRole | オートメーション実行時の実行ロールを指定 | 前述の権限が必要 |
OfflineScript | BASE64エンコードされたPowerShellスクリプト | ASCIIコードの文字列を使う |
SubnetId | 一時インスタンスを配置するサブネットID | 幾つかの特殊文字列による指定も可能。SelectedInstanceSubnet と指定すると復旧対象と同じサブネットになる(デフォルト値) |
CreatePreRescueBackup | EC2Rescue実行前にバックアップを取るか否か | |
CreatePostRescueBackup | EC2Rescue実行後にバックアップを取るか否か | |
AllowEncryptedVolume | 対象EBSが暗号化されているか否か | 暗号化EBSの場合はTrueにする |
AssociatePublicIpAddress | 一時インスタンスにPublic IPを割り当てるか否か | Publicサブネットで復旧する場合はTrueにする |
HelperInstanceSecurityGroupId | 一時インスタンスに割り当てるセキュリティグループID | 未指定時はdefault セキュリティグループが使われる |
今回は
- 実行ロールは事前準備した
StartEC2RescueWorkflowRole
- 一時インスタンスのサブネットは復旧対象と同じに
- 実行前バックアップを取得
- 実行後バックアップは不要
- EBSボリュームは暗号化されているため AllowEncryptedVolume パラメーターを指定
- セキュリティグループはアウトバウント通信全許可のものを明示的に指定
という内容にしています。
指定内容に問題がなければ「Execute」をクリックしてオートメーションを実行します。
完了まで結構時間がかかるので待ちます。
また、処理中は下図の様に一時インスタンスが立ち上がり、復旧対象インスタンスは停止されます。
最終的にエラー無く完了すればOKです。
この時点で一時インスタンスが破棄され、復旧対象インスタンスは再起動されています。
復旧対象インスタンスにRDP接続してスクリプトの実行結果を確認すると無事C-00000291
で始まるファイルだけ削除されているのが確認できました。
C-00000291で始まるファイルだけ削除されている
バックアップはAMIの形で保存されます。
事前バックアップを取った場合の例
これで無事期待通りの復旧ができました。
最後に
以上となります。
本記事の内容が今回の障害対応および将来の対応に役立てば幸いです。